Try-with-resources in Java 7

try..with..resources是java7的一种新的异常处理机制,能够很容易的使用try…catch块正确关闭资源回收.

对于有资源回收管理的异常处理方式,老式学校的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static void printFile() throws IOException {
InputStream input = null;

try {
input = new FileInputStream("file.txt");//1

int data = input.read();//2
while(data != -1){
System.out.print((char) data);
data = input.read();//3
}
} finally {
if(input != null){
input.close();//4
}
}
}

有4处地方容易发生异常,你可以看到,三处在try块,1处在finally块.finally块总是会执行,不管try块是否有异常抛出,这意味着, inputstream是会关闭的或者说试图关闭.
inputstream的close() 方法意味着也会抛出异常,如果关闭失败了. 想象一下异常从try块跑出来,finally块会执行.当异常冲finally块抛出的时候,这个异常会不会上抛给访问栈?
从finally块抛出的异常会传播给访问栈,即使try块的异常可能是更相关的传播。

try-with-resources
在java7你能写试用try-with-resource结构的代码来改造上面的案例:

1
2
3
4
5
6
7
8
9
10
11
12

private static void printFileJava7() throws IOException {

try(FileInputStream input = new FileInputStream("file.txt")) {

int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
}
}

注意第一行,这里的try-with-resources结构, 变量FileInputStream被声明在大括号里面,另外一FileInputStream是一个实例分配给一个变量.
当try块执行完成,FileInputStream将自动关闭.这是因为FileInputStream实现了java.lang.AutoCloseable接口.所有实现该接口的类都可以放在try-with-resources结构中.

如果一个异常从try-with-resources块中抛出,fileinputstream已经关闭了,try块中抛出的异常将抛到外层.异常会被抑制.这刚好和开头的案例相反(使用旧有异常处理).

使用多个资源
也可以使用try-with-resources块处理多个资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static void printFileJava7() throws IOException {

try( FileInputStream input = new FileInputStream("file.txt");
BufferedInputStream bufferedInput = new BufferedInputStream(input)
) {

int data = bufferedInput.read();
while(data != -1){
System.out.print((char) data);
data = bufferedInput.read();
}
}
}

这个案例创建了两个资源通过try关键字后面的括号. FileInputStream 和 BufferedInputStream.两个资源都会在执行流离开try块后自动关闭.
资源会通过反向初始化顺序的关闭, BufferedInputStream 先关闭, FileInputStream后关闭.

自定义可关闭资源的实现
使用try-with-resources结构不仅仅适用于java内建类.也能通过实现java.lang.autocloseable接口实现自己类使用.
下面是AutoCloseable接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

public interface AutoClosable {

public void close() throws Exception;
}

public class MyAutoClosable implements AutoCloseable {

public void doIt() {
System.out.println("MyAutoClosable doing it!");
}

@Override
public void close() throws Exception {
System.out.println("MyAutoClosable closed!");
}
}

private static void myAutoClosable() throws Exception {

try(MyAutoClosable myAutoClosable = new MyAutoClosable()){
myAutoClosable.doIt();
}
}

//console output
MyAutoClosable doing it!
MyAutoClosable closed!

你可以看到,try-with-resources是一个十分强大的方式确保使用在try-catch中的资源能正常关闭,不管这个资源时自己创建的还是java自带的组件.
译者:如果用户在使用一个类似于事务处理的业务时,可以通过实现该接口,完成一致性处理.